﻿#region License
/*
Copyright (c) 2009, Open University of the Netherlands
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided 
that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the 
  following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
  the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of Open University of the Netherlands nor the names of its contributors may be 
  used to endorse or promote products derived from this software without specific prior written 
  permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/
#endregion

#region Changelog

//----------   ---   -------------------------------------------------------------------------------
//dd-mm-yyyy - who - description
//----------   ---   -------------------------------------------------------------------------------
//03-08-2009 - veg - Added Metadata export.
//           - veg - Escaped character entities in Metadata value (Copyrigth symbol only for now).
//22-09-2009 - veg - Worked on processinginstruciton to get it to match what moodle likes: 
//                   ie double quotes, uppercase UTF-8 and no standalone attribute.
//                 - Changed all booleans to true/false strings
//                 - Changed all dates to seconds since unix epoch (1-1-1970).
//                 - Added some tags missing and updagraded backup to a more recent moodle version.
//                 - Searched for a xsd schema for the moodle backup but still no luck!
//                 - Changed name of output to moodle.xml so moodle recognizes it.
//                 - Zipped output (code still inside the main program).
//                 - Made script use the ExportAs value instead of the NodeName.
//25-09-2009   veg - Cleaned code (export functions).
//23-11-2009   veg - Added ExportAs_Html()
//24-11-2009   veg - Debugged ExportAs_Resource (ie CreateResource). 
//26-11-2009   veg - Added complexiteit attribute as backup for studielast during export.
//NOTE             - Actual files might be placed in a subdirectory so they do not show up directly.
//----------   ---   -------------------------------------------------------------------------------
//TODO             - Add more escaped characters.
//                 - Move escape code into separate routine.
//                 - Look into the file move code when creating Title/Text Nodes.
//                 - Port other CreateXX functions.
//                 - Clean course_files before export.
//----------   ---   -------------------------------------------------------------------------------
//NOTES            - The dom parameter needs to be removed (silently changed in xml).
//                 - Watch case of XmlDocument functions.
//                 - Make sure type of vars are declared properly (like cid as Int32 cid).
//
//                 - Beware not to edit the deployed version of this script!
//                 - Debugger.Break halts the script but does not provide sourcecode.
//----------   ---   -------------------------------------------------------------------------------

#endregion Changelog

namespace EwbDesigner.NET
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using System.Collections.Specialized;
    using System.Globalization;
    using Ionic.Zip;
    using System.Security.Permissions;
    using System.Windows.Forms;
    using System.Collections.Generic;

    /// <summary>
    /// This script parses the instance.xml saved by the main program and walks all nodes in it. 
    ///
    /// It uses reflection based on node names or ExportedAs attribute values to call methods in this script.
    /// The result is that the function performing the export does not have to be aware of what is inside the 
    /// remainder of this script and what it's exporting.
    /// 
    /// Before and after the node walking it calls two more (hardcoded) methods to create a prolugue and clean/fixup.
    /// </summary>
    class MoodleExport
    {
        #region Fields

        private const string BLOCKS = @"/MOODLE_BACKUP/COURSE/BLOCKS";
        private const string COURSE = @"/MOODLE_BACKUP/COURSE";
        private const string DETAILS = @"/MOODLE_BACKUP/INFO/DETAILS";
        private const string GRADEBOOK = @"/MOODLE_BACKUP/COURSE/GRADEBOOK";
        private const string HEADER = @"/MOODLE_BACKUP/COURSE/HEADER";
        private const string INFO = @"/MOODLE_BACKUP/INFO";
        private const string LEFT = "l"; //Block Position
        private const string MOD = @"/MOODLE_BACKUP/INFO/DETAILS/MOD";
        private const string MODULES = @"/MOODLE_BACKUP/COURSE/MODULES";
        private const string MOODLE = @"/MOODLE_BACKUP";
        private const string QUESTION_CATEGORIES = @"/MOODLE_BACKUP/COURSE/QUESTION_CATEGORIES";
        private const string RIGHT = "r"; //Block Position
        private const string SECTIONS = @"/MOODLE_BACKUP/COURSE/SECTIONS";

        private static String BaseDirectory = ".";
        private static String ModelName = "";
        private static String Model = "";

        private static Char[] ListSeparator = CultureInfo.CurrentCulture.TextInfo.ListSeparator.ToCharArray();

        private static Dictionary<String, String> File2Zip = new Dictionary<String, String>();

        //static const string crlf = "\r\n";

        /// <summary>
        /// Input Document.
        /// </summary>
        private static XmlDocument doc = new XmlDocument();

        /// <summary>
        /// Generates unique ID'key.
        /// </summary>
        private static Int32 idcounter = 2; //Start above 0 and 1 boolean values..

        /// <summary>
        /// Top Node of Output Document.
        /// </summary>
        private static XmlNode mb = null;

        /// <summary>
        /// The current Opdracht being processed.
        /// </summary>
        //private static Int32 opdracht = -1;    

        /// <summary>
        /// Add a notifications block?
        /// </summary>
        private static Boolean notifications = false;

        /// <summary>
        /// Add a topic 0 forum?
        /// </summary>
        private static Boolean forum0 = true;

        /// <summary>
        /// Add a topic 0 resources?
        /// </summary>
        private static Boolean resources0 = true;

        /// <summary>
        /// Time of export.
        /// </summary>
        private static DateTime now;

        /// <summary>
        /// The current Section.
        /// </summary>
        private static Int32 section = -1;

        /// <summary>
        /// Used for Questions Export.
        /// </summary>
        private static int stamp = 0;

        /// <summary>
        /// Used for Questions Export.
        /// </summary>
        private static string stampguid = ""; //The stamp'key base guid.

        /// <summary>
        /// Output Document.
        /// </summary>
        private static XmlDocument xml = null;

        private static String Course_Files = Path.Combine(BaseDirectory, "course_files");

        #endregion Fields

        #region Properties

        #endregion Properties

        #region Methods

        /// <summary>
        /// Loads a String resource from the main executable, Also works from within compiled script.
        /// </summary>
        /// <param name="Resource">The resource to be loaded</param>
        /// <returns></returns>
        private static String LoadResource(String Resource)
        {
            String apppath = Application.ExecutablePath.Replace(".vshost", "");

            //Note: Be Aware that when debugging the Executable path is 'EwbDesigner.vshost.exe' instead of 'EwbDesigner.exe'
            Assembly asm = Assembly.LoadFrom(apppath);

            //Debugging code, dump all resources...
            //foreach (String res in asm.GetManifestResourceNames())
            //{
            //    Debug.Print(res);
            //}

            if (asm != null && asm.GetManifestResourceInfo(Resource) != null)
            {
                //Assembly.GetCallingAssembly().GetManifestResourceInfo(Resource) != null)
                //Debug.Print("Loading: '{0}' from '{1}'", Resource, apppath);

                return new StreamReader(asm.GetManifestResourceStream(Resource)).ReadToEnd();
                //return new StreamReader(Assembly.GetEntryAssembly().GetManifestResourceStream(Resource)).ReadToEnd();
            }

            //Debug.Print("Resource '{0}' not found in '{1}'", Resource, apppath);

            return null;
        }

        /// <summary>
        /// Main Export Routine.
        /// </summary>
        /// <param name="model">Filename of the model to export</param>
        /// <returns>The resulting Zip archives filename</returns>
        public static String Execute(String model)
        {
            //Debug.Listeners.Add(new TextWriterTraceListener(Console.Out)); 

            Console.WriteLine("[Moodle Export]");
            Console.WriteLine("Instance=" + model);

            //Create a new XmlDocument (this does not give problems when executing the assembly twice).
            xml = new XmlDocument();

            ResetVariables(model);

            doc.Load(Model);

            xml.PreserveWhitespace = true;

            //Delete Moodle.xml or we will have problems with the XmlTextWriter later on.
            if (File.Exists(Path.Combine(BaseDirectory, "Moodle.xml")))
            {
                String s = Path.Combine(BaseDirectory, "Moodle.xml");
                File.Delete(s);
            }

            //Create Course_Files directory (and/or clean it)...
            if (Directory.Exists(Course_Files))
            {
                Directory.Delete(Course_Files, true);
            }
            Directory.CreateDirectory(Course_Files);

            //Create Xml Stub...
            CreateMoodleHeader();

            //Debug.WriteLine("[Nodes]");
            Recurse(doc.DocumentElement);
            //Debug.WriteLine(";End Nodes");

            CreateMoodleFooter();

            /*
             * Fails:
             * 
             * XmlDeclaration xmldecl = xml.CreateXmlDeclaration("1.0", null, null);
             * xmldecl.Encoding = "UTF-8";
             * xml.InsertBefore(xmldecl, xml.DocumentElement);
            */

            //Save indented XML with Linebreaks to make it more readable...
            using (XmlTextWriter writer = new XmlTextWriter("moodle.xml", System.Text.Encoding.UTF8))
            {
                writer.Formatting = Formatting.Indented;
                //Insert a Moodle Compatible processing instruction (Uppercase UTF-8, no standalone attribute & double quotes).
                writer.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");

                //Save Xml...
                xml.Save(writer);

                writer.Close();
            }

            //Remove Existing Zipfile.
            String zipfile = Path.Combine(Path.GetDirectoryName(model), "moodle_backup.zip");
            if (File.Exists(zipfile))
            {
                File.Delete(zipfile);
            }

            //Create new Zipfile.
            using (ZipFile zip = new ZipFile())
            {
                zip.AddFile(Path.Combine(Path.GetDirectoryName(model), "moodle.xml"), "");
                zip.AddDirectory(Path.Combine(Path.GetDirectoryName(model), "course_files"), "course_files");

                //foreach (KeyValuePair<String, String> fn in File2Zip)
                //{
                //    //Key = Full Filename, 
                //    //value = Moodle Path.
                //    Debug.WriteLine(fn.Key);
                //    Debug.WriteLine(fn.Value);
                //    zip.AddFile(fn.Key, fn.Value);
                //}

                zip.Save(zipfile);
            }

            Debug.WriteLine("Archive=" + zipfile);

            return zipfile;
        }

        private static void Recurse(XmlNode root)
        {
            Debug.Print(";Recursing Children of {0} <{1}>", root.NodeType.ToString(), root.Name);
            Debug.Indent();

            foreach (XmlNode node in root.ChildNodes)
            {
                /*
                 * Invoke Non Static Method
                 */
                //if (!HasAttribute(node, "ExportedAs"))
                //{
                //    Debug.Print("Warning {0} attribute is missing for <{1}>", "ExportedAs", node.Name);
                //}

                String ExportedAs = "ExportedAs_" + SafeGetAttribute(node, "ExportedAs", node.Name);

                // Get the desired method by name: DisplayName
                MethodInfo methodInfo = typeof(MoodleExport).GetMethod(ExportedAs);

                if (methodInfo != null)
                {
                    if (!HasAttribute(node, "ExportedAs"))
                    {
                        Debug.Print("*** Calling fallback {0}() for <{1}> ***", methodInfo.Name, node.Name);
                    }
                    else
                    {
                        Debug.Print("Calling {0}() for <{1}>", methodInfo.Name, node.Name);
                    }

                    // Use the instance to call the method without arguments

                    //Non Static...
                    //methodInfo.Invoke(this, null);

                    //Static...
                    object[] parameters = new object[1];
                    parameters[0] = node;
                    methodInfo.Invoke(null, parameters);
                }
                else
                {
                    Debug.Print("*** Export function {0}() is missing for <{1}> ***", ExportedAs, node.Name);
                }

                //Invoke Static Method
                /*
                Assembly assemblyInstance = Assembly.GetExecutingAssembly();
                Type thisClass = assemblyInstance.GetType("EwbDesigner.NET.MoodleExport", false, true);
                methodInfo = thisClass.GetMethod(node.Name);
                string key = "";
                if (methodInfo != null)
                {
                    object result = methodInfo.Invoke(null, null);
                    key = result.ToString();
                }
                */

                if (node.HasChildNodes)
                {
                    Recurse(node);
                }
            }
            Debug.Unindent();
        }

        private static void ResetVariables(string model)
        {
            idcounter = 1;                 //Generates unique ID'key
            now = DateTime.Now;            //The current Time of Export.
            section = -1;                   //The current Section.
            stampguid = "xyyz";            //The stamp'key base guid.
            //opdracht = -1;               //The current Opdracht being processed.

            Model = model;
            BaseDirectory = Path.GetDirectoryName(Model);
            ModelName = Path.GetFileNameWithoutExtension(Model);

            File2Zip.Clear();

            Course_Files = Path.Combine(BaseDirectory, "course_files");
        }
        #endregion Methods

        #region ExportAsHandlers

        //public static XmlNode ExportedAs_Fallback(XmlNode node, XmlNode parent)
        //{
        //    //TODO, hardcoded tekst attribute...

        //    return CreateTitleTextNode(node, SafeGetAttribute(node, "titel", "Error"), SafeGetAttribute(node, "tekst", "Error"), parent);
        //}

        public static void ExportedAs_EWB(XmlNode node)
        {
            String title = SafeGetAttribute(node, "titel", node.Name);
            String code = SafeGetAttribute(node, "code", node.Name);

            //Update Course Fullname and ShortName
            if (xml.DocumentElement.SelectSingleNode(HEADER + "/FULLNAME") != null)
            {
                xml.DocumentElement.SelectSingleNode(HEADER + "/FULLNAME").InnerText = title;
            }

            if (xml.DocumentElement.SelectSingleNode(HEADER + "/SHORTNAME") != null)
            {
                xml.DocumentElement.SelectSingleNode(HEADER + "/SHORTNAME").InnerText = code;
            }

            //veg: Should be numeric...
            xml.DocumentElement.SelectSingleNode(HEADER + "/METACOURSE").InnerText = BoolToInt(SafeGetAttribute(node, "metacourse", "false") == "true");


            if (SafeGetAttribute(node, "metacourse", "false") == "false")
            {
                xml.DocumentElement.SelectSingleNode(COURSE).RemoveChild(xml.DocumentElement.SelectSingleNode(COURSE + "/METACOURSE"));
            }
            else
            {
                //<METACOURSE>
                //<CHILDS>
                //  <CHILD>
                //    <ID>57</ID>
                //    <IDNUMBER></IDNUMBER>
                //    <SHORTNAME>A00000_2</SHORTNAME>
                //  </CHILD>
                //</CHILDS>
                //</METACOURSE>
            }

            //Fill in the Summary of Section Zero.
            String xpath = SECTIONS + "/SECTION" + "/NUMBER[. = \"" + section + "\"]/../SUMMARY";
            XmlNode summary = xml.DocumentElement.SelectSingleNode(xpath);
            if (summary != null)
            {
                summary.InnerText = "<H2>" + title + "</H2>";
            }
        }

        public static void ExportedAs_Tasks(XmlNode node)
        {
            Debug.WriteLine(";Ignoring Tasks Wrapper");
        }

        public static void ExportedAs_Task(XmlNode node)
        {
            CreateTask(node, "titel");
        }

        public static void ExportedAs_Jit(XmlNode node)
        {
            CreateLabelNode(node, "titel");
            CreateFolderResource(node, "tekst", "tekst");
        }

        /// <summary>
        /// Export a Casus.
        /// Creates a Html Resource.
        /// Status : Checked.
        /// </summary>
        /// <param name="node">The intermediate format Node to export</param>
        public static void ExportedAs_TextResource(XmlNode node)
        {
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0", true));

            CreateTitleTextResource(node.Name, "titel", "tekst", indent);
        }

        /// <summary>
        /// Export a Casus.
        /// Creates a Html Resource.
        /// Status : Checked.
        /// </summary>
        /// <param name="node">The intermediate format Node to export</param>
        public static void ExportedAs_Text(XmlNode node)
        {
            CreateTitleTextNode(node, "titel", "tekst");
        }

        /// <summary>
        /// Export a Casus.
        /// Creates a Html Resource.
        /// Status : Checked.
        /// </summary>
        /// <param name="node">The intermediate format Node to export</param>
        public static void ExportedAs_Html(XmlNode node)
        {
            CreateTitleHtmlNode(node, "titel", "tekst");
        }

        public static void ExportedAs_List(XmlNode node)
        {
            CreateList(node, "titel", "leerdoel");
        }

        public static void ExportedAs_Resource(XmlNode node)
        {
            CreateFileResource(node, "titel", "bestand");
        }

        public static void ExportedAs_Metadata(XmlNode node)
        {
            CreateMetaData(node);
        }

        //public static void ExportedAs_Omschrijving(XmlNode node)
        //{
        //    CreateOmschrijving(node);
        //}

        public static void ExportedAs_Opdracht(XmlNode node)
        {
            CreateAssignment(node, "titel", "instructie");
        }

        #endregion ExportAsHandlers

        #region Helpers

        private static Int64 Epoch(DateTime dt)
        {
            TimeSpan ts = (dt.ToUniversalTime() - new DateTime(1970, 1, 1));
            return (Int64)ts.TotalSeconds;
        }

        /// <summary>
        /// Converts a Boolean into a String.
        /// </summary>
        /// <param name="b">The value to convert</param>
        /// <returns>"true" or "false".</returns>
        private static String BoolToString(Boolean b)
        {
            if (b)
            {
                return "true";
            }
            else
            {
                return "false";
            }
        }

        /// <summary>
        /// Converts a Boolean into a Integer.
        /// </summary>
        /// <param name="b">The value to convert</param>
        /// <returns>"1" or "0".</returns>
        private static String BoolToInt(Boolean b)
        {
            if (b)
            {
                return "1";
            }
            else
            {
                return "0";
            }
        }

        /// <summary>
        /// Safely tries to retrieve an attribute value. If not found it returns the default value.
        /// </summary>
        /// <param name="node">The XmlNode to retrieve an attribute from</param>
        /// <param name="Name">The Name of the attribute</param>
        /// <param name="defaultvalue">The default value in case the attibute is missing</param>
        /// <param name="silent">If true no warnings are emitted if the attribute is missing</param>
        /// <returns>The attribute value or the defaultvalue</returns>
        private static String SafeGetAttribute(XmlNode node, String Name, String defaultvalue, Boolean silent)
        {
            if (node.Attributes.GetNamedItem(Name) != null)
            {
                return node.Attributes.GetNamedItem(Name).Value;
            }
            else
            {
                if (silent)
                {
                    Debug.Indent();
                    Debug.Print("*** Attribute {0}.{1} is missing, using default value '{2}' ***", node.Name, Name, defaultvalue);
                    Debug.Unindent();
                }
                return defaultvalue;
            }
        }

        /// <summary>
        /// Safely tries to retrieve an attribute value. If not found it returns the default value.
        /// </summary>
        /// <param name="node">The XmlNode to retrieve an attribute from</param>
        /// <param name="Name">The Name of the attribute</param>
        /// <param name="defaultvalue">The default value in case the attibute is missing</param>
        /// <returns>The attribute value or the defaultvalue</returns>
        private static String SafeGetAttribute(XmlNode node, String Name, String defaultvalue)
        {
            return SafeGetAttribute(node, Name, defaultvalue, false);
        }

        /// <summary>
        /// Checks if an attribute is present.
        /// </summary>
        /// <param name="node">The XmlNode to retrieve an attribute from</param>
        /// <param name="Name">The Name of the attribute</param>
        /// <returns>true if the attribute is present</returns>
        private static Boolean HasAttribute(XmlNode node, String Name)
        {
            return node.Attributes.GetNamedItem(Name) != null;
        }

        #endregion Helpers

        #region ExportAsWorkers

        /// <summary>
        /// Generate a List from a StringList.
        /// </summary>
        /// <param name="node">The node to process</param>
        /// <param name="titleatt">The name of the attribute holding the code</param>
        /// <param name="listattr">The name of the attribute holding the filename</param>
        private static void CreateList(XmlNode node, String titleattr, String listattr)
        {
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            String text = "";

            text += "<H2>" + SafeGetAttribute(node, titleattr, node.Name) + ":</H2><P />\r\n";

            text += "<UL title=\"" + SafeGetAttribute(node, titleattr, node.Name) + "\">";

            //Debugger.Break();

            String list = SafeGetAttribute(node, listattr, "");
            String[] sa = list.Split(ListSeparator);

            foreach (String value in sa)
            {
                text += String.Format("\r\n<LI>{0}</LI>", value);
            }
            text += "\r\n</UL>\r\n";

            CreateTitleTextNode(node.Name, SafeGetAttribute(node, titleattr, node.Name), text, indent);
        }

        /// <summary>
        /// Generate a Resource from a Filename.
        /// </summary>
        /// <param name="node">The node to process</param>
        /// <param name="titleatt">The name of the attribute holding the code</param>
        /// <param name="resattr">The name of the attribute holding the filename</param>
        private static void CreateFileResource(XmlNode node, String titleattr, String resattr)
        {
            String title = SafeGetAttribute(node, titleattr, node.Name);
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            //Boolean firstfile = true;

            //Emit a List of Files to a Directory.

            ////////////////////////////////////////////////////////////////////////////////
            //(1)
            //if we don't do it here we have a clash between a casus and the (file) resources.
            Int32 rid = idcounter++;

            Int32 hid = 0;

            //{ Start Loop;
            String res = SafeGetAttribute(node, resattr, "");
            String resfn = Path.GetFileName(res);
            String resdir = String.Format("resources_{0}", rid.ToString());

            //if (!File2Zip.ContainsKey(res))
            //{
            //File2Zip.Add(res, "course_files\\" + resdir + "\\" + node.Name + "\\" + resfn);
            //}

            //Create Course_Files directory (and/or clean it)...
            if (!Directory.Exists(Course_Files + "\\" + resdir))
            {
                Directory.CreateDirectory(Course_Files + "\\" + resdir);
            }

            CheckModule(DETAILS + "/MOD[NAME=\"resource\"]", "resource");

            _appendINSTANCEtoMODULE("resource", rid, node.Name, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //(2)
            _appendMODtoSECTION(idcounter++, section, "resource", rid, now, "0", indent, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //Add resource Module.
            ////////////////////////////////////////////////////////////////////////////////
            //(3)
            //Directory Resource...
            //Actual files in \course_files\sub, dus niet in \moddata\resource
            XmlNode mod = _appendNP(MODULES, "MOD");
            _appendNAT(mod, "ID", rid.ToString());
            _appendNAT(mod, "MODTYPE", "resource");
            _appendNAT(mod, "NAME", node.Name);
            _appendNAT(mod, "TYPE", "directory");
            _appendNAT(mod, "REFERENCE", "resources_" + rid.ToString());
            _appendNAT(mod, "SUMMARY", node.Name);
            _appendN(mod, "ALLTEXT");
            _appendN(mod, "POPUP");
            _appendN(mod, "OPTIONS");
            _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());

            //firstfile = false;

            if (res != "" && File.Exists(res))
            {
                //Create a redirecting html file pointing towards the resource.

                Debug.WriteLine(String.Format("Added Resource '{0}'", res));
                Debug.WriteLine(String.Format("            as '{0}'", Course_Files + "\\" + resdir + "\\" + resfn));

                File.Copy(res, Course_Files + "\\" + resdir + "\\" + resfn, true);

                TextWriter tw = new StreamWriter(String.Format(Course_Files + "\\" + resdir + "\\{0}{1}.htm", node.Name, hid));

                tw.WriteLine("<html>");
                tw.WriteLine(" <head>");
                tw.WriteLine("  <title>Redirect</title>");
                tw.WriteLine("  <meta http-equiv=\"Cache-Control\" content=\"no-cache\" />");
                tw.WriteLine("  <meta http-equiv=\"Pragma\" content=\"no-cache\" />");
                //tw.WriteLine("  <meta http-equiv=\"Expires\" content=\"0\" />");

                //TODO: node.Name seems empty
                tw.WriteLine("  <meta http-equiv=\"refresh\" content=\"0;URL=..\\" + resdir + "\\" + resfn + "\">");
                tw.WriteLine(" </head>");
                tw.WriteLine(" <body bgcolor=\"#ffffff\">");

                String url = "..\\" + resdir + "\\" + resfn;
                url = String.Join("/", url.Split(new char[] { '\\' }));
                tw.WriteLine("Externe Resource (<A HREF=\"" + url + "\">" + title + "</A>) wordt geladen...");

                tw.WriteLine(" </body>");
                tw.WriteLine("</html>");
                tw.Close();
            }
            else
            {
                TextWriter tw = new StreamWriter(String.Format(Course_Files + "\\" + resdir + "\\{0}{1}.htm", node.Name, hid));

                tw.WriteLine("<html>");
                tw.WriteLine(" <head>");
                tw.WriteLine("  <title>Redirect</title>");
                tw.WriteLine("  <meta http-equiv=\"Cache-Control\" content=\"no-cache\" />");
                tw.WriteLine("  <meta http-equiv=\"Pragma\" content=\"no-cache\" />");
                //tw.WriteLine("  <meta http-equiv=\"Expires\" content=\"0\" />");
                tw.WriteLine("  <meta http-equiv=\"refresh\" content=\"0;URL=" + res + "\">");
                tw.WriteLine(" </head>");
                tw.WriteLine(" <body bgcolor=\"#ffffff\">");
                tw.WriteLine("Externe Resource (<A HREF=\"" + res + "\">" + title + "</A>) wordt geladen...");
                tw.WriteLine(" </body>");
                tw.WriteLine("</html>");

                tw.Close();
            }
            hid++;
            //} End Loop;
            //CreateTitleTextNode(node.Name, SafeGetAttribute(node, titleattr, node.Name), SafeGetAttribute(node, resattr, "None"));
        }

        private static void CreateFolderResource(XmlNode node, String titleattr, String textattr)
        {
            String title = SafeGetAttribute(node, titleattr, node.Name);
            String text = SafeGetAttribute(node, textattr, "");
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            Int32 rid = idcounter++;
            _appendMODtoSECTION(idcounter++, section, "resource", rid, now, "0", indent, true, false);

            CheckModule(DETAILS + "/MOD[NAME=\"resource\"]", "resource");

            _appendINSTANCEtoMODULE("resource", rid, node.Name, true, false);

            XmlNode mod = _appendNP(MODULES, "MOD");

            _appendNAT(mod, "ID", rid);
            _appendNAT(mod, "MODTYPE", "resource");
            _appendNAT(mod, "NAME", title);
            _appendNAT(mod, "TYPE", "directory");
            _appendNAT(mod, "REFERENCE", node.Name);
            if (String.IsNullOrEmpty(text))
            {
                _appendN(mod, "SUMMARY");
            }
            else
            {
                _appendNAT(mod, "SUMMARY", text);
            }
            _appendN(mod, "ALLTEXT");
            _appendN(mod, "POPUP");
            _appendN(mod, "OPTIONS");
            _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
        }

        private static void CreateMetaData(XmlNode node)
        {
            String html = "<UL title=\"Metadata\">";
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            foreach (XmlAttribute attr in node.Attributes)
            {
                if (attr.Name != "ExportedAs")
                {
                    //TODO Extract into a method of it'key Own.
                    String v = attr.Value;
                    v = v.Replace("©", "&#x00A9;");

                    html += String.Format("\r\n<LI><STRONG>{0}</STRONG>: {1}</LI>", attr.Name, v);
                }
            }
            html += "\r\n</UL>\r\n";

            CreateTitleTextNode("Metadata", "Metadata", html, indent);
        }

        private static void CreateTitleTextNode(XmlNode node, String titleattr, String textattr)
        {
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            CreateTitleTextNode(node.Name, SafeGetAttribute(node, titleattr, node.Name), SafeGetAttribute(node, textattr, textattr), indent);
        }

        private static void CreateTitleTextNode(String name, String title, String text, Int32 indent)
        {
            Int32 cid = idcounter++;

            CheckModule(DETAILS + "/MOD[NAME=\"label\"]", "label");

            _appendINSTANCEtoMODULE("label", cid, name, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //(2)
            _appendMODtoSECTION(idcounter++, section, "label", cid, now, "0", indent, true, false);

            XmlNode mod = _appendNP(MODULES, "MOD");
            _appendNAT(mod, "ID", cid.ToString());
            _appendNAT(mod, "MODTYPE", "label");
            _appendNAT(mod, "NAME", title);
            _appendNAT(mod, "CONTENT", String.Format("<H1>{0}</H1>{1}<P/>", title, text));
            _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
        }

        private static void CreateLabelNode(XmlNode node, String titleattr)
        {
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            CreateLabelNode(node.Name, SafeGetAttribute(node, titleattr, node.Name), indent);
        }

        private static void CreateLabelNode(String name, String title, Int32 indent)
        {
            Int32 cid = idcounter++;

            CheckModule(DETAILS + "/MOD[NAME=\"label\"]", "label");

            _appendINSTANCEtoMODULE("label", cid, name, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //(2)
            _appendMODtoSECTION(idcounter++, section, "label", cid, now, "0", indent, true, false);

            XmlNode mod = _appendNP(MODULES, "MOD");
            _appendNAT(mod, "ID", cid.ToString());
            _appendNAT(mod, "MODTYPE", "label");
            _appendNAT(mod, "NAME", title);
            _appendNAT(mod, "CONTENT", String.Format("<H1>{0}</H1>", title));
            _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
        }

        private static void CreateTitleTextResource(String name, String title, String text, Int32 indent)
        {
            ////////////////////////////////////////////////////////////////////////////////
            //(1)
            Int32 cid = idcounter++;

            CheckModule(DETAILS + "/MOD[NAME=\"resource\"]", "resource");

            _appendINSTANCEtoMODULE("resource", cid, name, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //(2)
            _appendMODtoSECTION(idcounter++, section, "resource", cid, now, "0", indent, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //Add resource Module.
            ////////////////////////////////////////////////////////////////////////////////
            //(3)

            //Directory Resource...
            //Actual files in \course_files\sub, dus niet in \moddata\resource
            XmlNode mod = _appendNP(MODULES, "MOD");
            _appendNAT(mod, "ID", cid.ToString());
            _appendNAT(mod, "MODTYPE", "resource");
            _appendNAT(mod, "NAME", name);
            _appendNAT(mod, "TYPE", "text");
            _appendNAT(mod, "REFERENCE", "");
            _appendNAT(mod, "SUMMARY", name + ".");
            _appendNAT(mod, "ALLTEXT", text);
            //_appendNAT(mod, "POPUP", "resizable=1,scrollbars=1,directories=1,location=1,menubar=1,toolbar=1,status=1,height=450,width=620");
            _appendN(mod, "POPUP");
            _appendN(mod, "OPTIONS");
            _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
        }

        private static void CreateTitleHtmlNode(XmlNode node, String titleattr, String htmlattr)
        {
            String name = node.Name;
            String title = SafeGetAttribute(node, titleattr, node.Name);
            String html = SafeGetAttribute(node, htmlattr, htmlattr);
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            ////////////////////////////////////////////////////////////////////////////////
            //(1)
            Int32 cid = idcounter++;

            CheckModule(DETAILS + "/MOD[NAME=\"resource\"]", "resource");

            _appendINSTANCEtoMODULE("resource", cid, name, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //(2)
            _appendMODtoSECTION(idcounter++, section, "resource", cid, now, "0", indent, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //Add resource Module.
            ////////////////////////////////////////////////////////////////////////////////
            //(3)

            //Html resource: located in 'course_files/resources_{cid}/{name}.html'...
            XmlNode mod = _appendNP(MODULES, "MOD");
            _appendNAT(mod, "ID", cid.ToString());
            _appendNAT(mod, "MODTYPE", "resource");
            _appendNAT(mod, "NAME", name);
            _appendNAT(mod, "TYPE", "file");
            _appendNAT(mod, "REFERENCE", "resources_" + cid + "/" + name + ".htm"); //was .xhtml
            _appendNAT(mod, "SUMMARY", name + ".");
            _appendN(mod, "ALLTEXT");
            //_appendNAT(mod, "POPUP", "resizable=1,scrollbars=1,directories=1,location=1,menubar=1,toolbar=1,status=1,height=450,width=620");
            _appendN(mod, "POPUP");
            _appendN(mod, "OPTIONS");
            _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());

            ////////////////////////////////////////////////////////////////////////////////
            //Write physical file
            ////////////////////////////////////////////////////////////////////////////////
            //(4)
            String Resource_Files = Path.Combine(Course_Files, String.Format("resources_{0}", cid));
            if (!Directory.Exists(Resource_Files))
            {
                Directory.CreateDirectory(Resource_Files);
            }

            //Emit Single File to a Directory.
            String Resource_File = Path.Combine(Resource_Files, name + ".htm");
            using (StreamWriter stream = new StreamWriter(Resource_File))
            {
                //TODO Should this be valid XHTML?
                stream.Write(String.Format("<H2>{0}</H2>\r\n", title));
                stream.Write(String.Format("<P>{0}</P>\r\n", html));
            }
        }

        private static void CreateTask(XmlNode node, String titleattr)
        {
            Debug.WriteLine(String.Format("Section: {0}", section.ToString()));

            String titel = SafeGetAttribute(node, titleattr, node.Name);
            String studielast = SafeGetAttribute(node, "studielast", SafeGetAttribute(node, "complexiteit", "0"));

            if (!String.IsNullOrEmpty(studielast) && studielast != "0")
            {
                titel += " (" + studielast + ")";
            }

            _appendSECTION(idcounter++, ++section, "<H2>" + titel + "</H2>", true);
        }

        private static void CreateOmschrijving(XmlNode node)
        {
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            //<xs:attribute xmlns:q1="pimv2" name="titel" type="xs:string" />
            //<xs:attribute xmlns:q1="pimv2" name="instructie" type="xs:string" />
            //<xs:attribute xmlns:q1="pimv2" name="type">
            //  <xs:simpleType>
            //    <xs:restriction base="xs:string">
            //      <xs:enumeration value="studeeropdracht" />
            //      <xs:enumeration value="aanvulopdracht" />
            //      <xs:enumeration value="luisteropdracht" />
            //      <xs:enumeration value="demonstratieopdracht" />
            //    </xs:restriction>
            //  </xs:simpleType>

            String title = SafeGetAttribute(node, "titel", node.Name);
            String instructie = SafeGetAttribute(node, "instructie", "instructie");
            String type = SafeGetAttribute(node, "type", "geen");

            String html = String.Format("<h2>Instructie voor {0}</h2>{1}", type, instructie);

            CreateTitleTextNode(node.Name, title, html, indent);
        }

        private static void CreateAssignment(XmlNode node, String titleattr, String textattr)
        {
            Int32 aid = idcounter++;

            String title = SafeGetAttribute(node, titleattr, node.Name);
            String text = SafeGetAttribute(node, textattr, "");
            Int32 indent = Int32.Parse(SafeGetAttribute(node, "indent", "0"));

            //xml.DocumentElement


            ////////////////////////////////////////////////////////////////////////////////
            //(1)

            CheckModule(DETAILS + "/MOD[NAME=\"assignment\"]", "assignment");

            _appendMODtoSECTION(idcounter++, section, "assignment", aid, now, "0", indent, true, false);

            ////////////////////////////////////////////////////////////////////////////////
            //(2)
            _appendINSTANCEtoMODULE("assignment", aid, title, true, true);

            ////////////////////////////////////////////////////////////////////////////////
            //(3)
            XmlNode mod = _appendNP(MODULES, "MOD");
            _appendNAT(mod, "ID", aid);
            _appendNAT(mod, "MODTYPE", "assignment");
            _appendNAT(mod, "NAME", title);
            _appendNAT(mod, "DESCRIPTION", text);
            _appendNAT(mod, "FORMAT", "1");
            _appendNAT(mod, "RESUBMIT", "0");
            _appendNAT(mod, "PREVENTLATE", "0");
            _appendNAT(mod, "EMAILTEACHERS", "0");
            _appendNAT(mod, "TYPE", "0");                                               //Online, offline??
            _appendNAT(mod, "VAR1", "0");
            _appendNAT(mod, "VAR2", "0");
            _appendNAT(mod, "VAR3", "0");
            _appendNAT(mod, "VAR4", "0");
            _appendNAT(mod, "VAR5", "0");
            _appendNAT(mod, "ASSIGNMENTTYPE", "offline");                               //TODO
            _appendNAT(mod, "MAXBYTES", "2097152");
            _appendNAT(mod, "TIMEDUE", "0");
            _appendNAT(mod, "TIMEAVAILABLE", "0");
            _appendNAT(mod, "GRADE", "0");
            _appendNAT(mod, "TIMEMODIFIED", now);
            _appendN(mod, "SUBMISSIONS");

            /*
             * NOT IMPLEMENTED YET, Fixed to Offline
            //4
            fn =""+ obj.Value(mini, "DATA", "Model");
            if (fn != "") {
              dbg.DebugPrint("Processing Model: "+fn);

              var fso = new ActiveXObject("Scripting.FileSystemObject");
              var fldr;
              var tf;

              //Beware current Directory is used. Better set by program.
              if (!fso.FolderExists(target + "moddata")) {
                fldr = fso.CreateFolder(target + "moddata");
              }

              if (!fso.FolderExists(target + "moddata\\assignment")) {
                fldr = fso.CreateFolder(target + "moddata\\assignment");
              }

              if (!fso.FolderExists(target + "moddata\\assignment\\" + aid)) {
                fldr = fso.CreateFolder(target + "moddata\\assignment\\" + aid);
              }

              if (!fso.FolderExists(target + "moddata\\assignment\\" + aid + "\\responses")) {
                fldr = fso.CreateFolder(target + "moddata\\assignment\\" + aid + "\\responses");
              }
                
              fso.CopyFile(fn, target + "moddata\\assignment\\" + aid + "\\responses\\");
            } else {
              dbg.DebugPrint(obj.TypeName);
              dbg.DebugPrint(obj.Cell(ini, "ModelGuidCol", i));
              dbg.DebugPrint(mrvf);

              var xml = obj.Xml(mrvf, obj.TypeName);

              if (xml!="") {
                ////////////////////////////////////////////////////////////////////////////////
                //Write physical file
                ////////////////////////////////////////////////////////////////////////////////
                var fso = new ActiveXObject("Scripting.FileSystemObject");
                var fldr;
                var tf;

                //Beware current Directory is used. Better set by program.
                if (!fso.FolderExists(target + "moddata")) {
                  fldr = fso.CreateFolder(target + "moddata");
                }

                if (!fso.FolderExists(target + "moddata\\assignment")) {
                  fldr = fso.CreateFolder(target + "moddata\\assignment");
                }

                if (!fso.FolderExists(target + "moddata\\assignment\\" + aid)) {
                  fldr = fso.CreateFolder(target + "moddata\\assignment\\" + aid);
                }

                if (!fso.FolderExists(target + "moddata\\assignment\\" + aid + "\\responses")) {
                  fldr = fso.CreateFolder(target + "moddata\\assignment\\" + aid + "\\responses");
                }

                //Emit Single File to a Directory.
                tf = fso.CreateTextFile(target + "moddata\\assignment\\" + aid + "\\responses\\Antwoordmodel.htm", true, false); //was .xhtml
                tf.Write(xml);
                tf.Close();

                dbg.DebugPrint("Generated Model Answer as "" + target + "moddata\\assignment\\" + aid + "\\responses\\Antwoordmodel.htm" + """);

                //Move Embedded Files...
                try {
                  fso.MoveFile(target + "temp\\" + filename + "_*.*", target + "course_files\\resources_" + cid);
                } catch(exception) {
                //Silence
                }
              } else {
                dbg.DebugPrint("Empty Answer Model (no file or title).");
              }
            }
          }
        }
        */
        }

        private static void CreateMoodleHeader()
        {
            //Fails (lowercase utf-8)...
            //xml.AppendChild(xml.CreateProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""));

            ////////////////////////////////////////////////////////////////////////////////
            //Create Basic Structure.
            ////////////////////////////////////////////////////////////////////////////////

            mb = xml.AppendChild(xml.CreateElement("MOODLE_BACKUP"));              //_appendN fails at this point.

            _appendNP(MOODLE, "INFO");
            _appendNP(MOODLE, "ROLES");
            _appendNP(MOODLE, "COURSE");
            _appendNP(COURSE, "HEADER");

            //veg: Only present if a metacourse, it signals the parent (non-meta) course.
            _appendNP(COURSE, "METACOURSE");

            //<METACOURSE>
            //<CHILDS>
            //  <CHILD>
            //    <ID>57</ID>
            //    <IDNUMBER></IDNUMBER>
            //    <SHORTNAME>A00000_2</SHORTNAME>
            //  </CHILD>
            //</CHILDS>
            //</METACOURSE>

            _appendNP(COURSE, "BLOCKS");
            _appendNP(COURSE, "SECTIONS");
            //_appendNP(COURSE, "USERS");
            //_appendNP(COURSE, "QUESTION_CATEGORIES");
            //_appendNP(COURSE, "SCALES");
            _appendNATP(COURSE, "GRADEBOOK", "");
            _appendNP(COURSE, "MODULES");
            _appendNATP(COURSE, "FORMATDATA", "");

            ////////////////////////////////////////////////////////////////////////////////
            //Add INFO Section.
            ////////////////////////////////////////////////////////////////////////////////

            _appendNATP(INFO, "NAME", @"backup-cf101-20060824-1226.zip");             //theorie.Value(theorie.IniMapping, 'ITEM','Titel')
            _appendNATP(INFO, "MOODLE_VERSION", @"2007021520");
            _appendNATP(INFO, "MOODLE_RELEASE", @"1.8.2+");
            _appendNATP(INFO, "BACKUP_VERSION", @"2007022100");
            _appendNATP(INFO, "BACKUP_RELEASE", @"1.8+");
            _appendNATP(INFO, "DATE", Epoch(now).ToString());

            _appendNATP(INFO, "ORIGINAL_WWWROOT", @"http://localhost/moodle");
            _appendNATP(INFO, "ZIP_METHOD", "internal");

            ////////////////////////////////////////////////////////////////////////////////
            //Needs to be after above tags.
            ////////////////////////////////////////////////////////////////////////////////

            _appendNP(INFO, "DETAILS");

            ////////////////////////////////////////////////////////////////////////////////
            //Add HEADER Section..
            ////////////////////////////////////////////////////////////////////////////////

            Int32 pid = idcounter++;
            _appendNATP(HEADER, "ID", pid);
            _appendNP(HEADER, "CATEGORY");
            _appendNATP(HEADER + "/CATEGORY", "ID", idcounter++);
            _appendNATP(HEADER + "/CATEGORY", "NAME", "Miscellaneous");              //??
            _appendNATP(HEADER, "PASSWORD", "");
            _appendNATP(HEADER, "FULLNAME", Path.GetFileNameWithoutExtension(Model));
            _appendNATP(HEADER, "SHORTNAME", Path.GetFileNameWithoutExtension(Model));
            _appendNP(HEADER, "IDNUMBER");
            _appendNATP(HEADER, "SUMMARY", "Beschrijving");
            _appendNATP(HEADER, "FORMAT", "topics");                                //was iframe/topic
            _appendNATP(HEADER, "SHOWGRADES", BoolToInt(false));
            _appendNATP(HEADER, "NEWSITEMS", "5");
            _appendNATP(HEADER, "TEACHER", "Begeleider");
            _appendNATP(HEADER, "TEACHERS", "Begeleiders");
            _appendNATP(HEADER, "STUDENT", "Student");
            _appendNATP(HEADER, "STUDENTS", "Studenten");
            _appendNATP(HEADER, "GUEST", "0");
            _appendNATP(HEADER, "STARTDATE", now);
            //Not found in 1.8+ exports...
            //_appendNATP(HEADER, "ENROLPERIOD", "0");
            _appendNATP(HEADER, "NUMSECTIONS", "2");
            _appendNATP(HEADER, "MAXBYTES", "2097152");
            _appendNATP(HEADER, "SHOWREPORTS", BoolToInt(false));
            _appendNATP(HEADER, "GROUPMODE", BoolToInt(false));
            _appendNATP(HEADER, "GROUPMODEFORCE", BoolToInt(false));
            //_appendNATP(HEADER, "LANG", "nl_utf8");
            _appendNP(HEADER, "LANG");
            _appendNP(HEADER, "THEME");
            _appendNP(HEADER, "COST");
            _appendNATP(HEADER, "CURRENCY", "USD");
            _appendNATP(HEADER, "MARKER", BoolToInt(false));
            _appendNATP(HEADER, "VISIBLE", BoolToInt(false));
            _appendNATP(HEADER, "HIDDENSECTIONS", BoolToInt(false));
            _appendNATP(HEADER, "TIMECREATED", Epoch(now).ToString());
            _appendNATP(HEADER, "TIMEMODIFIED", Epoch(now).ToString());

            //This one signals if it's a MetaCourse...
            _appendNATP(HEADER, "METACOURSE", BoolToInt(false));

            _appendNATP(HEADER, "EXPIRENOTIFY", BoolToInt(false));
            _appendNATP(HEADER, "NOTIFYSTUDENTS", BoolToInt(false));
            _appendNATP(HEADER, "EXPIRYTHRESHOLD", "864000");
            _appendNATP(HEADER, "ENROLLABLE", BoolToInt(true));
            _appendNATP(HEADER, "ENROLSTARTDATE", "0");
            _appendNATP(HEADER, "ENROLENDDATE", "0");
            _appendNATP(HEADER, "ENROLPERIOD", "0");
            _appendNATP(HEADER, "ROLES_OVERRIDES", "");
            _appendNATP(HEADER, "ROLES_ASSIGNMENTS", "");

            ////////////////////////////////////////////////////////////////////////////////
            //Add BLOCK Sections
            ////////////////////////////////////////////////////////////////////////////////

            _appendBLOCK(pid, "yui_menu", "course-view", LEFT, 0, true,
                @"Tzo2OiJvYmplY3QiOjIxOntzOjEzOiJpdGVtX2NhbGVuZGFyIjtzOjQ6ImhpZGUiO3M6MTQ6Iml0ZW1fZ3JhZGVib29rIjtzOjQ6ImhpZGUiO3M6MTM6Iml0ZW1fbWVzc2FnZXMiO3M6NDoiaGlkZSI7czoxNDoiaXRlbV9tb2RfZm9ydW0iO3M6NDoiaGlkZSI7czoxNzoiaXRlbV9tb2RfcmVzb3VyY2UiO3M6NDoiaGlkZSI7czoxOToiaXRlbV9tb2RfYXNzaWdubWVudCI7czo0OiJoaWRlIjtzOjEzOiJpdGVtX21vZF9xdWl6IjtzOjQ6ImhpZGUiO3M6MTI6Iml0ZW1fb3V0bGluZSI7czo0OiJzaG93IjtzOjE3OiJpdGVtX3BhcnRpY2lwYW50cyI7czo0OiJoaWRlIjtzOjc6Im1heHNpemUiO3M6MjoiMjUiO3M6NToidHJ1bmMiO3M6MzoiLi4uIjtzOjExOiJpbnRyb2FjdGlvbiI7czo5OiJpbnRyb2hpZGUiO3M6Nzoib3JkZXJfMCI7czo4OiJjYWxlbmRhciI7czo3OiJvcmRlcl8xIjtzOjk6ImdyYWRlYm9vayI7czo3OiJvcmRlcl8yIjtzOjg6Im1lc3NhZ2VzIjtzOjc6Im9yZGVyXzMiO3M6OToibW9kX2ZvcnVtIjtzOjc6Im9yZGVyXzQiO3M6MTI6Im1vZF9yZXNvdXJjZSI7czo3OiJvcmRlcl81IjtzOjE0OiJtb2RfYXNzaWdubWVudCI7czo3OiJvcmRlcl82IjtzOjg6Im1vZF9xdWl6IjtzOjc6Im9yZGVyXzciO3M6Nzoib3V0bGluZSI7czo3OiJvcmRlcl84IjtzOjEyOiJwYXJ0aWNpcGFudHMiO30="
                );             //enabled
            _appendBLOCK(pid, "dynamic_course_list", "course-view", LEFT, 1, true,
                @"Tzo4OiJzdGRDbGFzcyI6Mjp7czo5OiJjb3Vyc2VfaWQiO3M6MjoiNTQiO3M6NToidGl0bGUiO3M6MTY6IlV3IHRpdGVsIGhpZXIuLi4iO30="
                );             //enabled

            _appendBLOCK(pid, "admin", "course-view", LEFT, 2, true);                   //enabled

            if (notifications)
            {
                _appendBLOCK(pid, "notification", "course-view", LEFT, 2, true);        //disabled
            }

            ////////////////////////////////////////////////////////////////////////////////
            //Add SECTION 0.
            ////////////////////////////////////////////////////////////////////////////////

            _appendSECTION(idcounter++, ++section, "SECTION", true);

            ////////////////////////////////////////////////////////////////////////////////
            //Add FORUM instance to MODULES.
            ////////////////////////////////////////////////////////////////////////////////

            if (forum0)
            {
                Int32 fid = idcounter++;

                _appendMODtoSECTION(idcounter++, section, "forum", fid, now, "0", 0, true, false);
                CheckModule(DETAILS + "/MOD[NAME=\"forum\"]", "forum");
                _appendINSTANCEtoMODULE("forum", fid, "Nieuwsforum", true, false);

                XmlNode mod = _appendNP(MODULES, "MOD");

                _appendNAT(mod, "ID", fid);
                _appendNAT(mod, "MODTYPE", "forum");
                _appendNAT(mod, "TYPE", "news");
                _appendNAT(mod, "NAME", "Nieuwsberichten");
                _appendNAT(mod, "INTRO", "Algemeen nieuws");
                //_appendNAT(mod, "OPEN", "1");
                _appendNAT(mod, "ASSESSED", "0");
                //_appendNAT(mod, "ASSESSPUBLIC", "0");
                _appendNAT(mod, "ASSESSTIMESTART", "0");
                _appendNAT(mod, "ASSESSTIMEFINISH", "0");
                _appendNAT(mod, "MAXBYTES", "0");
                _appendNAT(mod, "SCALE", "0");
                _appendNAT(mod, "FORCESUBSCRIBE", "1");
                _appendNAT(mod, "TRACKINGTYPE", "1");
                _appendNAT(mod, "RSSTYPE", "0");
                _appendNAT(mod, "RSSARTICLES", "0");
                _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
                _appendNAT(mod, "WARNAFTER", "0");
                _appendNAT(mod, "BLOCKAFTER", "0");
                _appendNAT(mod, "BLOCKPERIOD", "0");
            }

            ////////////////////////////////////////////////////////////////////////////////
            //Add BRONNEN instance to MODULES.
            ////////////////////////////////////////////////////////////////////////////////

            if (resources0)
            {
                Int32 rid = idcounter++;
                _appendMODtoSECTION(idcounter++, section, "resource", rid, now, "0", 0, true, false);

                CheckModule(DETAILS + "/MOD[NAME=\"resource\"]", "resource");

                _appendINSTANCEtoMODULE("resource", rid, "Bronnen", true, false);

                XmlNode mod = _appendNP(MODULES, "MOD");

                _appendNAT(mod, "ID", rid);
                _appendNAT(mod, "MODTYPE", "resource");
                _appendNAT(mod, "NAME", "Bronnen");
                _appendNAT(mod, "TYPE", "directory");
                _appendNAT(mod, "REFERENCE", "Bronnen");
                _appendNAT(mod, "SUMMARY", "Bronnen bij deze cursus");
                _appendN(mod, "ALLTEXT");
                _appendN(mod, "POPUP");
                _appendN(mod, "OPTIONS");
                _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
            }

            ////////////////////////////////////////////////////////////////////////////////
            //Add NOTIFICATIONS instance to MODULES.
            ////////////////////////////////////////////////////////////////////////////////

            if (notifications)
            {
                Int32 nid = idcounter++;

                _appendMODtoSECTION(idcounter++, section, "notifications", nid, now, "0", 0, true, false);

                CheckModule(DETAILS + "/MOD[NAME=\"notifications\"]", "notifications");

                _appendINSTANCEtoMODULE("notifications", nid, "Notifications", true, false);

                XmlNode mod3 = _appendNP(MODULES, "MOD");

                _appendNAT(mod3, "ID", nid.ToString());
                _appendNAT(mod3, "MODTYPE", "notifications");
                _appendNAT(mod3, "NAME", "Notificaties");
                _appendN(mod3, "CONTENT");
                _appendNAT(mod3, "TIMEMODIFIED", Epoch(now).ToString());
            }
        }

        private static void CreateMoodleFooter()
        {
            //Trailing Stuff....

            //METACOURSE maken aan de waarde in de header, maar dan numeriek
            _appendNATP(DETAILS, "METACOURSE", BoolToString(xml.DocumentElement.SelectSingleNode(HEADER + "/METACOURSE").InnerText.Equals("1")));

            _appendNATP(DETAILS, "USERS", "none");
            _appendNATP(DETAILS, "LOGS", BoolToString(false));
            _appendNATP(DETAILS, "USERFILES", BoolToString(false));     //was true
            _appendNATP(DETAILS, "COURSEFILES", BoolToString(true));    //was true
            _appendNATP(DETAILS, "MESSAGES", BoolToString(false));
            _appendNATP(DETAILS, "BLOCKFORMAT", "instances");
        }

        #endregion ExportAsWorkers

        #region Obsoleted

        //NOT Ported!

        //[Obsoleted]
        //private static void CreateCriteria(XmlNode node)
        //{
        //    Debug.WriteLine("TODO: Implement Workshop");
        //    /*
        //    while (workshopsids.length>0) {
        //        var wsid = workshopsids.pop()+'';

        //        var node = dom.documentElement.selectSingleNode(MODULES+'/MOD[ID='+wsid+'][MODTYPE="workshop"]');
        //        if (node) {
        //          ini = obj.IniMapping;

        //          _appendT(dom.documentElement.selectSingleNode(MODULES+'/MOD[ID='+wsid+'][MODTYPE="workshop"]/NELEMENTS'), obj.RowCount(ini));                   //from editor (no of criteria)

        //          if (obj.RowCount(ini)>0) {
        //            _appendT(dom.documentElement.selectSingleNode(MODULES+'/MOD[ID='+wsid+'][MODTYPE="workshop"]/NATTACHMENTS'), '1');                            //from editor (no of criteria)
        //          } else {
        //            _appendT(dom.documentElement.selectSingleNode(MODULES+'/MOD[ID='+wsid+'][MODTYPE="workshop"]/NATTACHMENTS'), '0');                            //from editor (no of criteria)
        //          }

        //          var es = dom.documentElement.selectSingleNode(MODULES+'/MOD[ID='+wsid+'][MODTYPE="workshop"]/ELEMENTS')
        //          for (i=0; i< obj.RowCount(ini); i++) {
        //            var e = _appendN(es, 'ELEMENT');

        //            _appendNAT(e, 'ELEMENTNO', i);
        //            _appendNAT(e, 'DESCRIPTION', obj.Cell(ini, 'ValueCol', i));
        //            _appendNAT(e, 'SCALE', '5');
        //            _appendNAT(e, 'MAXSCORE', '4');
        //            _appendNAT(e, 'WEIGHT', '11');
        //            _appendNAT(e, 'STDDEV', '0');
        //            _appendNAT(e, 'TOTALASSESSMENTS', '0');
        //          }
        //        }
        //    }
        //    */
        //}

        //NOT Ported!

        //[Obsoleted]
        //private static void CreateLeerkern(XmlNode node)
        //{
        //    Debug.WriteLine("TODO: Implement Assignment/Workshop");
        //    /*
        //    if (opdracht != tagToetsOpdracht) {
        //      _renderLeerkernAsAssigment(obj, dom);
        //    } else {
        //      _renderLeerkernAsWorkshop(obj, dom);
        //    }

        //    */
        //}

        //Partially Ported!

        //[Obsoleted]
        //private static void CreateOpdracht(XmlNode node, String titleattr)
        //{
        //    //TODO Cache opdracht type to differentiate tagLeerkern for tagToetsOpdracht.
        //    //opdracht = obj.Type;

        //    Int32 lid = idcounter++;

        //    String titel = SafeGetAttribute(node, titleattr, node.Name);
        //    String studielast = SafeGetAttribute(node, "studielast", "0");

        //    if (!String.IsNullOrEmpty(studielast) && studielast != "0")
        //    {
        //        titel += " (" + studielast + ")";
        //    }

        //    ////////////////////////////////////////////////////////////////////////////////
        //    //(1)
        //    CheckModule(DETAILS + "/MOD[NAME=\"label\"]", "label");

        //    _appendMODtoSECTION(idcounter++,section,  "label", lid, now, "0", 0, true, false);

        //    ////////////////////////////////////////////////////////////////////////////////
        //    //(2)
        //    _appendINSTANCEtoMODULE("label", lid, titel, true, false);

        //    ////////////////////////////////////////////////////////////////////////////////
        //    //(3)
        //    XmlNode mod = _appendNP(MODULES, "MOD");
        //    _appendNAT(mod, "ID", lid);
        //    _appendNAT(mod, "MODTYPE", "label");
        //    _appendNAT(mod, "NAME", /*obj.TypeName+": " + */titel);
        //    _appendNAT(mod, "CONTENT", "<h2>&nbsp;</h2>\r\n<h2>" + titel + "</h2>");
        //    _appendNAT(mod, "TIMEMODIFIED", Epoch(now).ToString());
        //}

        //Partially Ported!

        //[Obsoleted]
        //private static void CreateStudeeraanwijzingen(XmlNode node)
        //{
        //      String aanw = "";

        //      //for (i = 0; i<obj.RowCount(ini); i++ ) {
        //      //   if (obj.Cell(ini, "TitleCol", i)!="") {
        //      //      aanw = aanw + "<h3>" + obj.Cell(ini, "TitleCol", i) + "</h3>\r\n";
        //      //      rvf = obj.Mapping(".rvf", obj.Cell(ini, "GuidCol", i));
        //      //      if (obj.Body(rvf)!="") {
        //      //        aanw = aanw + obj.Body(rvf) + crlf;
        //      //      }
        //      //   }
        //      //}

        //      if (aanw!="") {
        //        aanw = "<h2>Studeeraanwijzing(en):</h2><p />\r\n" + aanw + "\r\n";
        //      } else {
        //        return;
        //      }

        //      ////////////////////////////////////////////////////////////////////////////////
        //      //(1)
        //      cid=idcounter++;

        //      node = dom.documentElement.selectSingleNode(DETAILS+"/MOD[NAME=\"resource\"]");
        //      if (!node) {
        //        _appendMOD("resource", true, false);
        //      }

        //      _appendINSTANCEtoMODULE("resource", cid, obj.TypeName, true, false);

        //      ////////////////////////////////////////////////////////////////////////////////
        //      //(2)
        //      _appendMODtoSECTION(idcounter++,section,  "resource", cid, now, 0, 0, true, false);

        //      ////////////////////////////////////////////////////////////////////////////////
        //      //Add resource Module.
        //      ////////////////////////////////////////////////////////////////////////////////
        //      //(3)

        //      //Directory Resource...
        //      //Actual files in \course_files\sub, dus niet in \moddata\resource
        //      XnlNode mod = _appendNP(MODULES, "MOD");
        //      _appendNAT(mod, "ID", cid);
        //      _appendNAT(mod, "MODTYPE", "resource");
        //      _appendNAT(mod, "NAME", node.Name);
        //      _appendNAT(mod, "TYPE", "title");
        //      _appendN  (mod, "REFERENCE");
        //      _appendNAT(mod, "SUMMARY", node.Name + ".");
        //      _appendNAT(mod, "ALLTEXT", aanw);
        //    //_appendNAT(mod, "POPUP", "resizable=1,scrollbars=1,directories=1,location=1,menubar=1,toolbar=1,status=1,height=450,width=620");
        //      _appendN  (mod, "POPUP");
        //      _appendN  (mod, "OPTIONS");
        //      _appendNAT(mod, "TIMEMODIFIED", now);
        //}

        //NOT Ported!

        //private static void CreateToetsopgaven(XmlNode node)
        //{
        //    Debug.WriteLine("TODO: Implement CreateToetsopgaven");
        //    /*

        //      var ini  = obj.IniMapping;

        //      if (obj.RowCount(ini)>0) {

        //        qsid=idcounter++;

        //        ////////////////////////////////////////////////////////////////////////////////
        //        //(1)
        //        node = dom.documentElement.selectSingleNode(DETAILS+'/MOD[NAME="quiz"]');
        //        if (!node) {
        //          _appendMOD(dom, 'quiz', true, false);
        //        }

        //        _appendMODtoSECTION(idcounter++,  section, 'quiz', qsid, now, 0, 0, true, false);

        //        ////////////////////////////////////////////////////////////////////////////////
        //        //(2)
        //        _appendINSTANCEtoMODULE(dom, 'quiz', qsid, obj.TypeName, true, false);

        //        ////////////////////////////////////////////////////////////////////////////////
        //        //(3)
        //         mod = _appendNP(dom, MODULES, 'MOD');
        //        _appendNAT(mod, 'ID', qsid);
        //        _appendNAT(mod, 'MODTYPE', 'quiz');
        //        _appendNAT(mod, 'NAME', obj.TypeName);
        //        _appendNAT(mod, 'INTRO', '<h2>' + obj.TypeName+ '</h2>');
        //        _appendNAT(mod, 'TIMEOPEN', '0');
        //        _appendNAT(mod, 'TIMECLOSE', '0');
        //        _appendNAT(mod, 'OPTIONFLAGS', '0');
        //        _appendNAT(mod, 'PENALTYSCHEME', '0');
        //        _appendNAT(mod, 'ATTEMPTS_NUMBER', '0');
        //        _appendNAT(mod, 'ATTEMPTONLAST', '0');
        //        _appendNAT(mod, 'GRADEMETHOD', '1');
        //        _appendNAT(mod, 'DECIMALPOINTS', '1');
        //        _appendNAT(mod, 'REVIEW', '62415');                                         //??
        //        _appendNAT(mod, 'QUESTIONSPERPAGE', '0');
        //        _appendNAT(mod, 'SHUFFLEQUESTIONS', '0');
        //        _appendNAT(mod, 'SHUFFLEANSWERS', '1');
        //        qqq = _appendN(mod, 'QUESTIONS');
        //        _appendNAT(mod, 'SUMGRADES', '2');                                          //??
        //        _appendNAT(mod, 'GRADE', '10');
        //        _appendNAT(mod, 'TIMECREATED', '0');
        //        _appendNAT(mod, 'TIMEMODIFIED', now);
        //        _appendNAT(mod, 'TIMELIMIT', '0');
        //        _appendN  (mod, 'PASSWORD');
        //        _appendN  (mod, 'SUBNET');
        //        _appendNAT(mod, 'POPUP', '0');
        //        _appendNAT(mod, 'DELAY1', '0');
        //        _appendNAT(mod, 'DELAY2', '0');
        //        qis = _appendN  (mod, 'QUESTION_INSTANCES');

        //        qids = '';

        //        ////////////////////////////////////////////////////////////////////////////////
        //        //(4) - Append Questions themselves.

        //        var qc = dom.documentElement.selectSingleNode(QUESTION_CATEGORIES+'/QUESTION_CATEGORY');
        //        if (!qc) {
        //          qc = _appendNP(dom, QUESTION_CATEGORIES, 'QUESTION_CATEGORY');
        //        }

        //        _appendNAT(qc, 'ID', idcounter++);
        //        _appendNAT(qc, 'NAME', 'Default');
        //        _appendNAT(qc, 'INFO', 'The default category for questions.');
        //        _appendNAT(qc, 'PUBLISH', '0');
        //        _appendNAT(qc, 'STAMP', _getStamp());
        //        _appendNAT(qc, 'PARENT', '0');
        //        _appendNAT(qc, 'SORTORDER', '999');

        //        //QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS
        //        qs = _appendN(qc, 'QUESTIONS');

        //        for (i=0; i<obj.RowCount(ini); i++) {
        //          qid = idcounter++;

        //          guid = obj.Cell(ini, 'GuidCol', i);

        //          basedir = obj.Mapping('', guid);

        //          afini = obj.Mapping('.ini', guid, basedir);   //Answers Grid
        //          afrvf = obj.Mapping('.rvf', guid, basedir);   //Questions

        //          //QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS/QUESTION
        //          q = _appendN(qs, 'QUESTION');
        //          _appendNAT(q, 'ID', qid);
        //          _appendNAT(q, 'PARENT', '0');
        //          _appendNAT(q, 'NAME', obj.Cell(ini, 'TitleCol', i));
        //          _appendNAT(q, 'QUESTIONTEXT',obj.body(afrvf));
        //          _appendNAT(q, 'QUESTIONTEXTFORMAT', '1');
        //          _appendN  (q, 'IMAGE');
        //          _appendNAT(q, 'DEFAULTGRADE', '1');
        //          _appendNAT(q, 'PENALTY', '0.1');
        //          if (obj.Cell(ini, 'TypeCol', i) == 'Multiple Choice') {
        //            _appendNAT(q, 'QTYPE', 'multichoice');
        //          } else if (obj.Cell(ini, 'TypeCol', i) == 'Open') {
        //            _appendNAT(q, 'QTYPE', 'essay');
        //          }
        //          _appendNAT(q, 'LENGTH', '1');
        //          _appendNAT(q, 'STAMP', _getStamp());
        //          _appendNAT(q, 'VERSION', _getStamp());
        //          _appendNAT(q, 'HIDDEN', '0');

        //          //append question instance to quiz module
        //          //MODULES/MOD/QUESTION_INSTANCE
        //          var qi = _appendN(qis,'QUESTION_INSTANCE');
        //          _appendNAT(qi, 'ID', idcounter++);
        //          _appendNAT(qi, 'QUESTION', qid);

        //          _appendNAT(qi, 'GRADE', '1');

        //          if (qids!='') {
        //             qids = qids+ ',' + qid;
        //          } else {
        //             qids = qid;
        //          }

        //          //QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS/QUESTION/MULTICHOICE
        //          if (obj.Cell(ini, 'TypeCol', i) == 'Multiple Choice') {
        //            m = _appendN(q, 'MULTICHOICE');

        //            _appendNAT(m, 'LAYOUT', '0');

        //            //Make list of Answer ID'key on beforehand.
        //            lst = '';
        //            for (j=0; j<obj.RowCount(afini);j++) {
        //              if (lst!='') {
        //                 lst = lst + ',' + (idcounter + j);
        //              } else {
        //                 lst = idcounter + j;
        //              }
        //            }
        //            _appendNAT(m, 'ANSWERS', lst);

        //            //Count sectionnumber of correct Answers.
        //            cor = 0;
        //            for (j=0; j<obj.RowCount(afini);j++) {
        //              if (obj.Cell(afini, 'CorrectCol', j)=='True') {
        //                cor++;
        //              }
        //            }

        //            if (cor>1) {
        //              _appendNAT(m, 'SINGLE', '0');
        //            } else {
        //              _appendNAT(m, 'SINGLE', '1');
        //            }
        //            _appendNAT(m, 'SHUFFLEANSWERS', '0');

        //            //QUESTION_CATEGORIES/QUESTION_CATEGORY/QUESTIONS/QUESTION/ANSWERS
        //            as = _appendN(q, 'ANSWERS')
        //            for (j=0; j<obj.RowCount(afini);j++) {
        //              aa = _appendN(as, 'ANSWER');
        //              _appendNAT(aa, 'ID', idcounter++);
        //              rvf=obj.Mapping('.rvf', obj.Cell(afini, 'AntwoordGuidCol', j), basedir);
        //              _appendNAT(aa, 'ANSWER_TEXT', obj.body(rvf));
        //              _appendNAT(aa, 'FRACTION', obj.Cell(afini, 'ScoreCol', j));
        //              rvf=obj.Mapping('.rvf', obj.Cell(afini, 'FeedbackGuidCol', j), basedir);
        //              _appendNAT(aa, 'FEEDBACK', obj.body(rvf));
        //            }
        //          } else if (obj.Cell(ini, 'TypeCol', i) == 'Open') {
        //            as = _appendN(q, 'ANSWERS')
        //            for (j=0; j<obj.RowCount(afini);j++) {
        //              aa = _appendN(as, 'ANSWER');
        //              _appendNAT(aa, 'ID', idcounter++);
        //              //Ignore answer title as it should be plain title....
        //              //rvf=obj.Mapping('.rvf', obj.Cell(afini, 'AntwoordGuidCol', j), basedir);
        //              //_appendNAT(aa, 'ANSWER_TEXT', obj.body(rvf));
        //              _appendNAT(aa, 'ANSWER_TEXT', '*');
        //              _appendNAT(aa, 'FRACTION', obj.Cell(afini, 'ScoreCol', j));
        //              //Although moodle doesn't support it in edit mode, html just works...
        //              rvf=obj.Mapping('.rvf', obj.Cell(afini, 'FeedbackGuidCol', j), basedir);
        //              _appendNAT(aa, 'FEEDBACK', obj.body(rvf));
        //            }
        //          }
        //        }
        //        _appendT(qqq, qids + ',0');
        //      }
        //     */
        //}

        //private static void CreateBronnen(XmlNode node)
        //{
        //    Debug.WriteLine("TODO: Implement CreateBronnen");
        //    /*
        //    if (opdracht == -1)
        //    {
        //        _renderAllResources(obj, dom);
        //    }
        //    else
        //    {
        //        _renderResources(obj, dom);
        //    }
        //    */
        //}

        //[Obsolete("No Indent Support")]
        //private static void CreateTitleTextNode(String name, String code, String title)
        //{
        //    CreateTitleTextNode(name, code, title, 0);
        //}

        //[Obsolete("No Indent Support")]
        //private static void CreateLabelNode(String name, String code)
        //{
        //    CreateLabelNode(name, code, 0);
        //}

        //[Obsolete("No Indent Support")]
        //private static void CreateTitleTextResource(String name, String code, String title)
        //{
        //    CreateTitleTextResource(name, code, title, 0);
        //}

        #endregion Obsoleted

        #region XmlGeneration

        //Append Attribute + Value to node.
        private static XmlNode _appendAttribute(XmlNode node, String name, String value)
        {
            XmlAttribute att = node.OwnerDocument.CreateAttribute(name);

            att.Value = value;

            return node.Attributes.SetNamedItem(att);
        }

        //Append BLOCK to /MOODLE_BACKUP/COURSE/BLOCKS.
        private static XmlNode _appendBLOCK(Int32 pageid, String name, String pagetype, String position, Int32 weight, Boolean visible, String configdata)
        {
            XmlNode node = xml.DocumentElement.SelectSingleNode(BLOCKS);

            XmlNode mod3 = _appendN(node, "BLOCK");

            _appendNAT(mod3, "ID", idcounter++);
            _appendNAT(mod3, "NAME", name);
            _appendNAT(mod3, "PAGEID", pageid);
            _appendNAT(mod3, "PAGETYPE", pagetype);
            _appendNAT(mod3, "POSITION", position);
            _appendNAT(mod3, "WEIGHT", weight);
            _appendNAT(mod3, "VISIBLE", BoolToInt(visible));
            if (String.IsNullOrEmpty(configdata))
            {
                _appendN(mod3, "CONFIGDATA");
            }
            else
            {
                _appendNAT(mod3, "CONFIGDATA", configdata);
            }
            _appendNAT(mod3, "ROLES_OVERRIDES", "");

            return _appendNAT(mod3, "ROLES_ASSIGNMENTS", "");
        }

        //Append BLOCK to /MOODLE_BACKUP/COURSE/BLOCKS.
        private static XmlNode _appendBLOCK(Int32 pageid, String name, String pagetype, String position, Int32 weight, Boolean visible)
        {
            return _appendBLOCK(pageid, name, pagetype, position, weight, visible, null);
        }

        //Append INSTANCE to /MOODLE_BACKUP/INFO/DETAILS/MOD/INSTANCES.
        private static XmlNode _appendINSTANCEtoMODULE(String type, Int32 id, String name, Boolean included, Boolean userinfo)
        {
            String xpath = MOD + "[NAME=\"" + type + "\"]/INSTANCES";

            XmlNode node = xml.DocumentElement.SelectSingleNode(xpath);

            XmlNode mod2 = _appendN(node, "INSTANCE");

            _appendNAT(mod2, "ID", id);
            _appendNAT(mod2, "NAME", name);
            _appendNAT(mod2, "INCLUDED", BoolToString(included));
            _appendNAT(mod2, "USERINFO", BoolToString(userinfo));

            return mod2;
        }

        //Append MOD to /MOODLE_BACKUP/INFO/DETAILS.
        private static XmlNode _appendMOD(String name, Boolean included, Boolean userinfo)
        {
            XmlNode node = xml.DocumentElement.SelectSingleNode(DETAILS);

            XmlNode mod1 = _appendN(node, "MOD");

            _appendNAT(mod1, "NAME", name);
            _appendNAT(mod1, "INCLUDED", BoolToString(included));
            _appendNAT(mod1, "USERINFO", BoolToString(userinfo));

            return _appendN(mod1, "INSTANCES");
        }

        //Append MOD to /MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS
        private static XmlNode _appendMODtoSECTION(Int32 modid, Int32 sectionnumber, String type, Int32 instance, DateTime added, String score, Int32 indent, Boolean visible, Boolean groupmode)
        {
            String xpath = SECTIONS + "/SECTION[NUMBER=\"" + sectionnumber + "\"]/MODS";

            XmlNode node = xml.DocumentElement.SelectSingleNode(xpath);

            XmlNode mod5 = _appendN(node, "MOD");

            _appendNAT(mod5, "ID", modid);
            _appendNAT(mod5, "TYPE", type);
            _appendNAT(mod5, "INSTANCE", instance);
            //_appendNAT(mod5, "DATE", Epoch(added).ToString());
            _appendNAT(mod5, "ADDED", added);
            _appendNAT(mod5, "SCORE", score);
            _appendNAT(mod5, "INDENT", indent);
            _appendNAT(mod5, "VISIBLE", BoolToInt(visible));
            _appendNAT(mod5, "GROUPMODE", BoolToInt(groupmode));
            _appendN(mod5, "ROLES_OVERRIDES");
            _appendN(mod5, "ROLES_ASSIGNMENTS");

            return mod5;
        }

        //Append <NAME> to node.
        private static XmlNode _appendN(XmlNode node, String name)
        {
            return node.AppendChild(node.OwnerDocument.CreateElement(name));
        }

        //Append <NAME> and TEXT to node.
        private static XmlNode _appendNAT(XmlNode node, String name, String text)
        {
            if (String.IsNullOrEmpty(text))
            {
                return _appendN(node, name);
            }
            else
            {
                return node.AppendChild(node.OwnerDocument.CreateElement(name)).AppendChild(node.OwnerDocument.CreateTextNode(text));
            }
        }

        //Append <NAME> and TEXT to node.
        private static XmlNode _appendNAT(XmlNode node, String name, Int32 num)
        {
            return node.AppendChild(node.OwnerDocument.CreateElement(name)).AppendChild(node.OwnerDocument.CreateTextNode(num.ToString()));
        }

        //Append <NAME> and TEXT to node.
        private static XmlNode _appendNAT(XmlNode node, String name, DateTime date)
        {
            return node.AppendChild(node.OwnerDocument.CreateElement(name)).AppendChild(node.OwnerDocument.CreateTextNode(Epoch(date).ToString()));
        }

        //Append <NAME> & TEXT to XPATH node.
        private static XmlNode _appendNATP(String xpath, String name, String text)
        {
            XmlNode node = xml.DocumentElement.SelectSingleNode(xpath);

            return node.AppendChild(xml.CreateElement(name)).AppendChild(xml.CreateTextNode(text));
        }

        //Append <NAME> & TEXT to XPATH node.
        private static XmlNode _appendNATP(String xpath, String name, Int32 num)
        {
            return _appendNATP(xpath, name, num.ToString());
        }

        //Append <NAME> & TEXT to XPATH node.
        private static XmlNode _appendNATP(String xpath, String name, DateTime date)
        {
            return _appendNATP(xpath, name, Epoch(date).ToString());
        }

        //Append <NAME> & TEXT to XPATH node.
        private static XmlNode _appendNATP(String xpath, String name, Boolean b)
        {
            return _appendNATP(xpath, name, BoolToInt(b));
        }

        //Append <NAME> to XPATH node.
        private static XmlNode _appendNP(String xpath, String name)
        {
            XmlNode node = xml.DocumentElement.SelectSingleNode(xpath);

            return node.AppendChild(xml.CreateElement(name));
        }

        //Append SECTION to /MOODLE_BACKUP/COURSE/SECTIONS.
        private static XmlNode _appendSECTION(Int32 sectionid, Int32 number, String summary, Boolean visible)
        {
            XmlNode node = xml.DocumentElement.SelectSingleNode(SECTIONS);

            XmlNode mod4 = _appendN(node, "SECTION");

            _appendNAT(mod4, "ID", sectionid);
            _appendNAT(mod4, "NUMBER", number);
            if (summary == "")
            {
                //_appendNAT(mod4, "SUMMARY", "Geen Samenvatting");
                _appendNAT(mod4, "SUMMARY", "");
            }
            else
            {
                _appendNAT(mod4, "SUMMARY", summary);
            }
            _appendNAT(mod4, "VISIBLE", BoolToInt(visible));

            //Update number of sections in export (Moodle does not show them).
            xml.DocumentElement.SelectSingleNode(HEADER + "/NUMSECTIONS").InnerText = number.ToString();

            return _appendN(mod4, "MODS");
        }

        //Append TEXT to node.
        private static XmlNode _appendT(XmlNode node, String text)
        {
            return node.AppendChild(node.OwnerDocument.CreateTextNode(text));
        }

        //Create a unique Stamp based on a GUID.
        private static String _getStamp()
        {
            return '_' + stampguid + '+' + (stamp++) + '_';
        }

        /// <summary>
        /// Checks if a Mod(ule) is present. If not it's created.
        /// </summary>
        /// <param name="XPath">The Xpath query pointing to the Mod(ule)</param>
        /// <param name="Name">The Name of the Mod(ule) in case it needs to be added.</param>
        private static void CheckModule(String XPath, String Name)
        {
            if (xml.DocumentElement.SelectSingleNode(XPath) == null)
            {
                _appendMOD(Name, true, false);
            }
        }

        #endregion XmlGeneration
    }
}
